#include "bspent.h"

BspEnt::BspEnt()
{
}

BspEnt::~BspEnt()
{
}

void BspEnt::load(const std::string& map)
{
	std::ifstream is(map.c_str(), std::ios_base::in | std::ios_base::binary);

	if (!is)
		throw Exception("Can't open file");
	
	if (!check_magic(is))
		throw Exception("Bad file type, or bad version");
	

	std::pair<int, int> ent_info = entities_info(is);

	get_nodes(ent_info, is);
}

void BspEnt::get_nodes(const std::pair<int, int>& ent_info, std::ifstream& is)
{
	std::stringstream ss;
	char buffer[1025];
	int counter = 0;

	is.seekg(ent_info.first, std::ios_base::beg);
	while (counter != ent_info.second)
	{
		memset(buffer, 0, 1025);
		is.read(buffer, std::min(1024, ent_info.second - counter));
		ss << buffer;
		counter += is.gcount();
		if (is.gcount() == 0)
			break;
	}

	parse(ss);
}

void BspEnt::parse(std::stringstream& ss)
{
	bool done = false;
	char c;

	while(!done)
	{
		ss.read(&c, 1);
		if (ss.gcount() != 1)
		{
			done = true;
			continue;
		}
		if (c <= 32)
			continue;
		if (c == '{')
		{
			std::pair<bool, std::map<std::string, std::string> > p;
			p = parse_acc(ss);

			if (p.first == true)
				break;
			nodes__.push_back(p.second);
		}
	}
}

std::pair<bool, std::map<std::string, std::string> > BspEnt::parse_acc(std::stringstream& ss)
{
	bool done = false;
	std::string s;
	std::string first;
	std::string second;
	char c;
	int counter = 0;
	std::map<std::string, std::string> m;

	while(!done)
	{
		ss.read(&c, 1);
		if (ss.gcount() != 1)
		{
			done = true;
			continue;
		}
		if (c == '}')
			break;
		if (c <= 32)
			continue;
		if (c == '"')
		{
			done = parse_str(ss, s);
			if (done)
				break;

			if (counter % 2 == 0)
				first = s;
			else
			{
				second = s;
				m[first] = second;
			}
			s = "";
			counter++;
		}
	}

	return std::pair<bool, std::map<std::string, std::string> >(done, m);
}

bool BspEnt::parse_str(std::stringstream& ss, std::string& s)
{
	bool done = false;
	char c;

	while (!done)
	{
		ss.read(&c, 1);
		if (ss.gcount() != 1)
		{
			done = true;
			continue;
		}
		if (c == '"')
			break;
		else
			s += c;
	}

	return done;
}

std::pair<int, int> BspEnt::entities_info(std::ifstream& is)
{
	std::pair<int, int> ent;

	int tmp;
	is.read(reinterpret_cast<char*>(&tmp), 4);
	if (is.gcount() != 4)
		throw Exception("Problem reading file.");
	ent.first = tmp;
	is.read(reinterpret_cast<char*>(&tmp), 4);
	if (is.gcount() != 4)
		throw Exception("Problem reading file.");
	ent.second = tmp;
	
	return ent;
}

bool BspEnt::check_magic(std::ifstream& is)
{
	// Magic; must be "IBSP", without the '\0' at the end.
	char b[5] = {0, 0, 0, 0, 0};
	is.read(b, 4);
	if (is.gcount() != 4)
		throw Exception("Problem reading file.");

	// Version; must be 0x2e in our case, because I don't know how are the other versions
	int v;
	is.read(reinterpret_cast<char*>(&v), 4);
	if (is.gcount() != 4)
		throw Exception("Problem reading file.");
	
	return std::string("IBSP") == b && v == 0x2e;
}

std::list<std::map<std::string, std::string> >& BspEnt::getNodes()
{
	return nodes__;
}

irr::core::vector3df strToVector3df(const std::string& s)
{
	irr::core::vector3df v;
	std::stringstream ss;

	ss << s;
	ss >> v.X;
	ss >> v.Y;
	ss >> v.Z;

	return v;
}

irr::core::vector3df bspStrToVector3df(const std::string& s)
{
	irr::core::vector3df v;
	std::stringstream ss;

	ss << s;
	ss >> v.X;
	ss >> v.Z;
	ss >> v.Y;

	return v;
}

int strToInt(const std::string& s)
{
	std::stringstream ss;
	int i;
	
	ss << s;
	ss >> i;

	return i;
}

double strToDouble(const std::string& s)
{
	std::stringstream ss;
	double d;

	ss << s;
	ss >> d;

	return d;
}

/*
int main(int argc, char** argv)
{
	std::list<std::map<std::string, std::string> > m;
	std::string map = "20kdm2.bsp";
	if (argc == 2)
		map = argv[1];
	
	BspEnt b;
	b.load(map);

	m = b.getNodes();
	std::list<std::map<std::string, std::string> >::iterator it = m.begin();
	
	while (it != m.end())
	{
		//std::cout << "{" << std::endl;
		//std::map<std::string, std::string>::iterator it2 = (*it).begin();
		//while (it2 != (*it).end())
		//{
		//	std::cout << "\t\"" << it2->first << "\", \"" << it2->second << "\"" << std::endl;
		//	it2++;
		//}
		//
		//std::cout << "}" << std::endl;
		//

		std::cout << "Classname: " << (*it)["classname"] << std::endl;
		it++;
	}

	return 0;
}
*/

